import type { PageType } from '../../../../../lib/page_handler/type'
import type { AutoPagination, IPaginationSelectors } from '../index'
import { PageHandler } from '../../../../../lib/page_handler'
import { BaiduPageType } from '../../../../../lib/page_handler/platform/baidu/types'
import { BianPageType } from '../../../../../lib/page_handler/platform/bian/types'
import { GiteePageType } from '../../../../../lib/page_handler/platform/gitee/types'
import { GithubPageType } from '../../../../../lib/page_handler/platform/github/types'
import { GreasyForkPageType } from '../../../../../lib/page_handler/platform/greasyfork/types'
import { ToolKit } from '../../../../../lib/utils/toolkit'
import {
  getAllHideSelector,
  HOSTNAME,
  logger,
  SUPPORTED_PLATFORMS,
} from '../../../../../types/constants'
import { SYMBOLS } from '../../../../../types/interfaces'

type InsertionType = 'document-fragment' | 'batch' | 'direct'

// 插入状态接口
interface AutoPaginationInsertionState {
  pageNum: number
  status: 'pending' | 'inserting' | 'completed' | 'failed'
  totalItems: number
  insertedItems: number
  startTime: number
  endTime?: number
}

// 插入统计信息接口
interface InsertionStatistics {
  type: InsertionType
  startTime: number
  endTime?: number
  duration?: number
  totalItems: number
  batchInfo?: {
    current: number
    total: number
  }
}

interface ProgressUpdateOptions {
  insertionType: InsertionType
  startTime?: number
  batchIndex?: number
  totalBatches?: number
}

export class AutoPaginationResult {
  // @Decorators.Identity
  readonly name: string = 'AutoPaginationResult'
  // @Decorators.RequiredInitParam
  private selectors: IPaginationSelectors
  private paginationInstance: AutoPagination
  // @Decorators.PublicState
  private static instance: AutoPaginationResult
  // @Decorators.Dependency
  private readonly pageHandler: PageHandler = PageHandler.getInstance()
  // @Decorators.Core
  readonly pageType: PageType = PageHandler.getCurrentPageType()
  // @Decorators.UIElement
  private readonly newResultAttribute: string = 'data-is-new-result'
  // @Decorators.Config
  private readonly BATCH_INSERT_SIZE: number = 20 // 批量插入的 DOM 数量
  private readonly USE_DOCUMENT_FRAGMENT: boolean = true // 是否使用文档片段
  private readonly DEBOUNCE_DELAY: number = 50 // 防抖延迟(ms)
  // @Decorators.TemporaryState
  private debounceTimer?: number
  // 插入状态管理
  insertionTimeout: number = 5000 // 插入超时时间(ms)
  private insertionStates: Map<number, AutoPaginationInsertionState> = new Map()
  private insertionListeners: Map<number, Array<(state: AutoPaginationInsertionState) => void>> = new Map()

  constructor(paginationInstance: AutoPagination) {
    this.selectors = paginationInstance.selectors
    this.paginationInstance = paginationInstance
  }

  /**
   * 获取单例实例
   */
  public static getInstance(paginationInstance: AutoPagination): AutoPaginationResult {
    if (!AutoPaginationResult.instance) {
      AutoPaginationResult.instance = new AutoPaginationResult(paginationInstance)
    }
    return AutoPaginationResult.instance
  }

  /**
   * 设置插入状态
   */
  setInsertionState(pageNum: number, state: Partial<AutoPaginationInsertionState>): void {
    const currentState = this.insertionStates.get(pageNum) || {
      pageNum,
      status: 'pending',
      totalItems: 0,
      insertedItems: 0,
      startTime: Date.now(),
    }

    const newState = { ...currentState, ...state }
    this.insertionStates.set(pageNum, newState)

    // 通知监听器
    this.notifyInsertionListeners(pageNum, newState)
  }

  /**
   * 获取插入状态
   */
  private getInsertionState(pageNum: number): AutoPaginationInsertionState | undefined {
    return this.insertionStates.get(pageNum)
  }

  /**
   * 更新插入进度（支持插入类型和耗时统计）
   */
  private updateProgress(
    pageNum: number,
    insertedCount: number,
    totalItems: number,
    options: ProgressUpdateOptions = { insertionType: 'direct' },
  ): void {
    const currentState = this.getInsertionState(pageNum)
    if (!currentState)
      return

    const { insertionType, startTime, batchIndex, totalBatches } = options

    // 计算耗时
    const currentTime = Date.now()
    const duration = startTime ? currentTime - startTime : undefined

    // 准备更新数据
    const updateData: Partial<AutoPaginationInsertionState> = {
      insertedItems: insertedCount,
    }

    // 添加统计信息（仅在状态中存储，不持久化）
    const stats: InsertionStatistics = {
      type: insertionType,
      startTime: startTime || currentTime,
      totalItems,
      ...(duration && { duration }),
      ...(batchIndex !== undefined && totalBatches !== undefined && {
        batchInfo: { current: batchIndex, total: totalBatches },
      }),
    }

    // 检查是否完成
    if (insertedCount >= totalItems) {
      updateData.status = 'completed'
      updateData.endTime = currentTime

      // 记录最终统计
      this.logInsertionCompletion(pageNum, stats, currentTime)
    }

    this.setInsertionState(pageNum, updateData)
  }

  /**
   * 记录插入完成日志
   */
  private logInsertionCompletion(
    pageNum: number,
    stats: InsertionStatistics,
    endTime: number,
  ): void {
    const finalDuration = endTime - stats.startTime
    const itemsPerSecond = stats.totalItems / (finalDuration / 1000)

    logger.info(`[${this.name}] 第 ${pageNum} 页插入完成`, {
      type: this.getInsertionTypeText(stats.type),
      items: stats.totalItems,
      duration: `${finalDuration} ms`,
      performance: `${itemsPerSecond.toFixed(1)} 项/秒`,
      ...stats.batchInfo && {
        batch: `共 ${stats.batchInfo.total} 批`,
      },
    })
  }

  /**
   * 获取插入类型文本
   */
  private getInsertionTypeText(type: InsertionType): string {
    const typeMap = {
      'document-fragment': '文档片段',
      'batch': '分批插入',
      'direct': '直接插入',
    }
    return typeMap[type] || type
  }

  /**
   * 注册插入状态监听器
   */
  private onInsertionStateChange(pageNum: number, callback: (state: AutoPaginationInsertionState) => void): () => void {
    if (!this.insertionListeners.has(pageNum)) {
      this.insertionListeners.set(pageNum, [])
    }

    const listeners = this.insertionListeners.get(pageNum)!
    listeners.push(callback)

    // 返回取消监听函数
    return () => {
      const index = listeners.indexOf(callback)

      if (index > -1) {
        listeners.splice(index, 1)
      }
    }
  }

  /**
   * 通知监听器
   */
  private notifyInsertionListeners(pageNum: number, state: AutoPaginationInsertionState): void {
    const listeners = this.insertionListeners.get(pageNum) || []

    listeners.forEach((callback) => {
      try {
        callback(state)
      }
      catch (error: any) {
        logger.error(`[${this.name}] 插入状态监听器执行失败`, error)
      }
    })
  }

  /**
   * 等待插入完成
   */
  waitForInsertionComplete(pageNum: number, timeout?: number): Promise<AutoPaginationInsertionState> {
    if (!timeout)
      timeout = this.insertionTimeout

    return new Promise((resolve, reject) => {
      // 立即检查当前状态
      const checkCurrentState = (): boolean => {
        const currentState = this.getInsertionState(pageNum)

        if (currentState?.status === 'completed') {
          resolve(currentState)
          return true
        }

        if (currentState?.status === 'failed') {
          reject(new Error(`插入失败: 第 ${pageNum} 页`))
          return true
        }

        return false
      }

      // 立即检查一次
      if (checkCurrentState()) {
        return
      }

      let timeoutId: number
      let pollIntervalId: number

      // 轮询检查状态（用于处理监听器注册前的完成情况）
      const startPolling = (): void => {
        pollIntervalId = window.setInterval(() => {
          if (checkCurrentState()) {
            clearInterval(pollIntervalId)
            clearTimeout(timeoutId)
          }
        }, 100) // 每 100ms 轮询一次
      }

      // 状态变化监听（用于处理监听器注册后的状态变化）
      const cleanup = this.onInsertionStateChange(pageNum, (state) => {
        if (state.status === 'completed' || state.status === 'failed') {
          cleanup() // 移除监听器
          clearTimeout(timeoutId)

          if (state.status === 'completed') {
            resolve(state)
          }
          else {
            reject(new Error(`插入失败: 第 ${pageNum} 页`))
          }
        }
      })

      // 开始轮询
      startPolling()

      // 设置超时
      timeoutId = window.setTimeout(() => {
        cleanup()
        clearInterval(pollIntervalId)

        const finalState = this.getInsertionState(pageNum)
        if (finalState?.status === 'completed') {
        // 虽然超时，但实际已完成
          resolve(finalState)
        }
        else {
          reject(new Error(`插入超时: 第 ${pageNum} 页，超过 ${timeout} ms，当前状态: ${finalState?.status || 'unknown'}`))
        }
      }, timeout)
    })
  }

  /**
   * 查询插入状态
   */
  getInsertionStatus(pageNum: number): {
    status: string
    progress: number
    inserted: number
    total: number
    duration?: number
  } {
    const state = this.getInsertionState(pageNum)
    if (!state) {
      return { status: 'unknown', progress: 0, inserted: 0, total: 0 }
    }

    const progress = state.totalItems > 0
      ? Math.round((state.insertedItems / state.totalItems) * 100)
      : 0

    const duration = state.endTime ? state.endTime - state.startTime : undefined

    return {
      status: state.status,
      progress,
      inserted: state.insertedItems,
      total: state.totalItems,
      duration,
    }
  }

  /**
   * 检查是否所有插入都完成
   */
  isAllInsertionsComplete(): boolean {
    for (const state of this.insertionStates.values()) {
      if (state.status !== 'completed' && state.status !== 'failed') {
        return false
      }
    }
    return true
  }

  /**
   * 追加新结果
   */
  appendResults(pageNum: number, newDoc: Document, unique: boolean = true): void {
    const newItems = newDoc.querySelectorAll(this.selectors.item)

    // 设置插入开始状态
    this.setInsertionState(pageNum, {
      status: 'inserting',
      totalItems: newItems.length,
      insertedItems: 0,
      startTime: Date.now(),
    })

    try {
      const header = this.selectors?.header
        ? document.querySelector(this.selectors.header)
        : null
      const container = document.querySelector(this.selectors.container)

      if (!container) {
        this.setInsertionState(pageNum, {
          status: 'failed',
          endTime: Date.now(),
        })
        logger.warn(`[${this.name}] 未找到目标容器`, this.selectors.container)
        return
      }

      if (newItems.length === 0) {
        this.setInsertionState(pageNum, {
          status: 'completed',
          insertedItems: 0,
          endTime: Date.now(),
        })
        logger.info(`[${this.name}] 没有新内容可添加`, this.selectors.item)
        return
      }

      // 使用防抖避免频繁操作
      this.debouncedAppend(() => {
        if (!unique) {
          this.appendNonUniqueResults(pageNum, header, container, newItems)
        }
        else {
          this.appendUniqueResults(pageNum, header, container, newItems)
        }
      })
    }
    catch (error: any) {
      this.setInsertionState(pageNum, {
        status: 'failed',
        endTime: Date.now(),
      })
      logger.error(`[${this.name}] 追加结果失败`, error)
    }
  }

  private debouncedAppend(callback: () => void): void {
    if (this.debounceTimer) {
      clearTimeout(this.debounceTimer)
    }

    this.debounceTimer = window.setTimeout(() => {
      callback()
      this.debounceTimer = undefined
    }, this.DEBOUNCE_DELAY)
  }

  /**
   * 追加非唯一结果（不检查重复）
   */
  private appendNonUniqueResults(pageNum: number, header: Element | null, container: Element, newItems: NodeListOf<Element>): void {
    let insertedCount = 0
    const totalItems = newItems.length
    const startTime = Date.now()

    const updateProgress = () => {
      insertedCount++

      this.updateProgress(pageNum, insertedCount, totalItems, {
        insertionType: this.determineInsertionType(newItems.length),
        startTime,
      })
    }

    this.addPageHeader(pageNum, header)

    // 开始追加结果
    this.startAppend(container, newItems, false, updateProgress)
  }

  /**
   * 追加唯一结果（检查重复）
   */
  private appendUniqueResults(pageNum: number, header: Element | null, container: Element, newItems: NodeListOf<Element>): void {
    const existingHashes = new Set<string>()
    let insertedCount = 0
    const startTime = Date.now()

    // 收集现有内容的哈希值（不包含被隐藏的元素）
    container.querySelectorAll(`${this.selectors.item}:not(${getAllHideSelector()})`).forEach((item) => {
      existingHashes.add(this.generateContentHash(item))
    })

    if (ToolKit.set.isSetEmpty(existingHashes)) {
      logger.warn(`[${this.name}] 当前页面不支持生成内容哈希，无法添加新结果`)
    }

    const uniqueItems: Element[] = []
    let addedCount = 0

    // 筛选唯一项
    newItems.forEach((item) => {
      const itemHash = this.generateContentHash(item)

      if (!existingHashes.has(itemHash)) {
        item.setAttribute(this.newResultAttribute, 'true')
        existingHashes.add(itemHash)
        uniqueItems.push(item)
        addedCount++
      }
    })

    // 设置总项目数
    this.setInsertionState(pageNum, {
      totalItems: uniqueItems.length,
      insertedItems: 0,
    })

    // 没有新增内容时，执行无更多内容处理
    if (addedCount === 0) {
      this.setInsertionState(pageNum, {
        status: 'completed',
        insertedItems: 0,
        endTime: Date.now(),
      })
      this.paginationInstance.handleNoMoreContent()
      logger.notice(`[${this.name}] ${SYMBOLS.STATUS.WARNING} 没有新增内容`)
      return
    }
    else {
      // 有新增内容后，支持滚动加载
      this.paginationInstance.activePageEnd = false
    }

    const updateProgress = (): void => {
      insertedCount++

      this.updateProgress(pageNum, insertedCount, uniqueItems.length, {
        insertionType: this.determineInsertionType(uniqueItems.length),
        startTime,
      })
    }

    this.addPageHeader(this.paginationInstance.currentPageNum, header)

    // 开始追加结果
    this.startAppend(container, uniqueItems, true, updateProgress)
  }

  /**
   * 添加分页头
   */
  addPageHeader(pageNum: number, header: Element | null): void {
    // 添加分页分隔符
    const $separator = this.paginationInstance.addPageSeparator(pageNum)

    if ($separator && header) {
      this.paginationInstance.currentConfig.headerInsertPosition === 'beforePageSeparator'
        ? $separator.before(header)
        : $separator.after(header)
    }
  }

  /**
   * 开始追加结果
   */
  private startAppend(container: Element, uniqueItems: NodeListOf<Element> | Element[], uniqueId: boolean, updateProgress: () => void): void {
    const insertionType = this.determineInsertionType(uniqueItems.length)

    switch (insertionType) {
      // 使用文档片段批量插入（性能最佳）
      case 'document-fragment':
        this.appendWithDocumentFragment(container, uniqueItems, uniqueId, updateProgress)
        break
      // 分批插入（避免阻塞UI）
      case 'batch':
        this.appendInBatches(container, uniqueItems, uniqueId, updateProgress)
        break
      // 直接插入（数量少时性能更好）
      default:
        this.appendDirectly(container, uniqueItems, uniqueId, updateProgress)
        break
    }
  }

  /**
   * 根据项目数量确定插入类型
   */
  private determineInsertionType(itemCount: number): InsertionType {
    if (itemCount === 0)
      return 'direct'

    if (this.USE_DOCUMENT_FRAGMENT && itemCount > this.BATCH_INSERT_SIZE) {
      return 'document-fragment'
    }
    else if (itemCount > this.BATCH_INSERT_SIZE) {
      return 'batch'
    }
    else {
      return 'direct'
    }
  }

  /**
   * 生成内容哈希
   */
  private generateContentHash(element: Element): string {
    switch (HOSTNAME) {
      case SUPPORTED_PLATFORMS.BAI_DU:
        switch (this.pageType) {
          case BaiduPageType.Web:
            return `${element.id}-${element.getAttribute('mu') || ''}`
          default:
            return `${element.getAttribute('mu') || ''}`
        }
      case SUPPORTED_PLATFORMS.GREASY_FORK:
        switch (this.pageType) {
          case GreasyForkPageType.SCRIPT_LIST:
            return `${element.getAttribute('data-script-id') || ''}`
          case GreasyForkPageType.SCRIPT_VERSIONS: {
            const url = element.querySelector('.version-number > a')?.getAttribute('href') || ''
            const fullUrl = ToolKit.browser.url.getFullUrl(url)
            return this.pageHandler.services.greasyFork.extractVersionIdFromUrl(fullUrl)
          }
          case GreasyForkPageType.SCRIPT_FEEDBACK: {
            const url = element.querySelector('.discussion-title')?.getAttribute('href') || ''
            return this.pageHandler.services.greasyFork.extractDiscussionIdFromUrl(url)
          }
          default:
            return ''
        }

      case SUPPORTED_PLATFORMS.BIAN:
        switch (this.pageType) {
          case BianPageType.List: {
            const url = element.querySelector('a')?.getAttribute('href') || ''
            return url.match(/\/(\d+).html/)?.[1] || ''
          }
          default:
            return ''
        }

      case SUPPORTED_PLATFORMS.GITEE:
        switch (this.pageType) {
          case GiteePageType.Commits:
            return element.querySelector('.commit')?.getAttribute('data-id') || ''
          default:
            return ''
        }

      case SUPPORTED_PLATFORMS.GITHUB:
        switch (this.pageType) {
          case GithubPageType.Commits:
          case GithubPageType.Tags:
            return element.querySelector('.Details ul li a:has(> .octicon-git-commit)')?.textContent.trim() || ''
          default:
            return ''
        }
    }

    return ''
  }

  /**
   * 使用文档片段批量插入（性能最佳）
   */
  private appendWithDocumentFragment(
    container: Element,
    items: NodeListOf<Element> | Element[],
    isUnique: boolean,
    onProgress: () => void,
  ): void {
    const fragment = document.createDocumentFragment()
    const itemArray = Array.from(items)

    // 准备所有节点
    itemArray.forEach((item, index) => {
      if (isUnique && !item.hasAttribute(this.newResultAttribute)) {
        return
      }

      const cloned = this.prepareItemForInsertion(item, isUnique)
      fragment.appendChild(cloned)

      // 每插入一个项目更新进度
      onProgress()

      // 每处理一定数量后短暂让出主线程
      if (index % this.BATCH_INSERT_SIZE === 0) {
        this.yieldToMainThread()
      }
    })

    // 单次插入到DOM
    container.appendChild(fragment)
  }

  /**
   * 分批插入（避免长时间阻塞）
   */
  private appendInBatches(
    container: Element,
    items: NodeListOf<Element> | Element[],
    isUnique: boolean,
    onProgress: () => void,
  ): void {
    const itemArray = Array.from(items)
    const totalBatches = Math.ceil(itemArray.length / this.BATCH_INSERT_SIZE)
    let currentBatch = 0

    const insertNextBatch = () => {
      const startIndex = currentBatch * this.BATCH_INSERT_SIZE
      const endIndex = Math.min(startIndex + this.BATCH_INSERT_SIZE, itemArray.length)
      const batchItems = itemArray.slice(startIndex, endIndex)

      const fragment = document.createDocumentFragment()

      batchItems.forEach((item) => {
        if (isUnique && !item.hasAttribute(this.newResultAttribute)) {
          return
        }

        const cloned = this.prepareItemForInsertion(item, isUnique)
        fragment.appendChild(cloned)
        onProgress() // 更新进度
      })

      container.appendChild(fragment)
      currentBatch++

      logger.info(`[${this.name}] [分批插入] 插入批次 ${currentBatch}/${totalBatches}`)

      if (currentBatch < totalBatches) {
        // 使用 requestAnimationFrame 分批插入，避免阻塞UI
        requestAnimationFrame(() => {
          setTimeout(insertNextBatch, 0) // 给浏览器喘息机会
        })
      }
      else {
        logger.info(`[${this.name}] [分批插入] 插入完成，共 ${itemArray.length} 项`)
      }
    }

    insertNextBatch()
  }

  /**
   * 直接插入（适用于少量项）
   */
  private appendDirectly(
    container: Element,
    items: NodeListOf<Element> | Element[],
    isUnique: boolean,
    onProgress: () => void,
  ): void {
    Array.from(items).forEach((item, index) => {
      if (isUnique && !item.hasAttribute(this.newResultAttribute)) {
        return
      }

      const cloned = this.prepareItemForInsertion(item, isUnique)
      container.appendChild(cloned)
      onProgress() // 更新进度

      // 少量插入时也可以适当让出主线程
      if (index % 5 === 0) {
        this.yieldToMainThread()
      }
    })
  }

  /**
   * 准备插入项
   */
  private prepareItemForInsertion(item: Element, isUnique: boolean): Node {
    // 克隆节点避免文档污染
    const cloned = item.cloneNode(true) as Element

    if (isUnique) {
      cloned.removeAttribute(this.newResultAttribute)
    }

    // 应用样式和动画
    this.applyInsertionStyles(cloned)

    return cloned
  }

  /**
   * 应用插入样式和动画
   */
  private applyInsertionStyles(_element: Element): void {
  }

  /**
   * 让出主线程（避免长时间阻塞）
   */
  private yieldToMainThread(): Promise<unknown> {
    // 使用 Promise 让出主线程控制权
    return new Promise((resolve) => {
      setTimeout(resolve, 0)
    })
  }

  /**
   * 批量让出主线程（优化大量插入）
   */
  async yieldInBatches(operations: number, batchSize: number = 10): Promise<void> {
    for (let i = 0; i < operations; i += batchSize) {
      if (i > 0 && i % batchSize === 0) {
        await this.yieldToMainThread()
      }
    }
  }
}
